/*
 *  Copyright (C) 2010 Matthias Buch-Kromann <mbk.isv@cbs.dk>
 * 
 *  This file is part of the MatrixParser package.
 *  
 *  The MatrixParser program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.osdtsystem.matrixparser.featureextraction;

import java.util.HashSet;
import java.util.Set;
import org.osdtsystem.matrixparser.data.CONLLSentence;
import org.osdtsystem.matrixparser.data.CONLLToken;
import org.osdtsystem.matrixparser.parsers.output.DependencyTree;
import org.osdtsystem.matrixparser.features.FeatureVector;
import org.osdtsystem.matrixparser.parsers.ParsingModel;

/**
 *
 * @author Matthias Buch-Kromann <mbk.isv@cbs.dk>
 */
public class HigherOrderMbkCframes extends AbstractHigherOrderExtractor {
    final static String name = "cframes";
    final static int genes = 111;

    // Complement frames
    final static int[] cftimes = {-1,-2,-4,-8,-16,-1000,1,2,4,8,16,1000};
    final static int[] cftimesLastOnly = {cftimes[cftimes.length-1]};
    public static int[] offsets = positiveFilter(cftimes);
    final static Set<Integer> offsetsSet = intArrayAsSet(cftimes);
    
    public HigherOrderMbkCframes(ParsingModel model) {
        super(model, name, genes);
    }

    public void extractHigherOrder(FeatureVector fv, DependencyTree tree, int inode) {
        if (inode < 0 || inode >= tree.nodes())
            return;
        int gene = firstGene();

        // Find features for node
        CONLLSentence sentence = tree.sentence();
        CONLLToken node = sentence.get(inode);
        String postag = onMulti(gene++) ? node.postag() : "";
        String cpostag = onMulti(gene++) ? node.postag() : "";
        String form = onMulti(gene++) ? node.form() : "";
        String lemma = onMulti(gene++) ? node.lemma() : "";
        String label = onMulti(gene++) ? tree.labelName(inode) : "";

        //
        int time = onMulti(gene++) ? tree.time(inode) : 1000;
        time = onMulti(gene++) ? time : 0;

        // Add complement frame
        boolean useCftimes = onMulti(gene++);
        int geneSaved = gene;
        for (int cftime : useCftimes ? cftimes : cftimesLastOnly) {
            gene = geneSaved;

            // Only generate complement frame features before current cftime
            if (useCftimes && DependencyTree.isLaterThan(cftime, time))
                break;
            
            // Find complement frames and counts
            String lcframeno = onMulti(gene++) ? "" + tree.cframeCount(inode, cftime, tree.leftChildren(inode)) : "";
            String rcframeno = onMulti(gene++) ? "" + tree.cframeCount(inode, cftime, tree.rightChildren(inode)) : "";
            String cframeno = onMulti(gene++) ? lcframeno + ":" + rcframeno : "";
            String lcframe = onMulti(gene++) ? tree.cframeStringLeft(inode, cftime) : "";
            String rcframe = onMulti(gene++) ? tree.cframeStringRight(inode, cftime) : "";
            String cframe = onMulti(gene++) ? tree.cframeStringAll(inode, cftime) : "";
            
            // Generate features
            String[] frames = {cframe, lcframe, rcframe, lcframeno, rcframeno, cframeno};
            for (int f = 0; f < frames.length; ++f) {
                String frame = frames[f];
                String key = "cframe" + f;

                // Add features
                if (on(gene++)) add(fv, key, frame);
                if (on(gene++)) add(fv, key +"l", frame, label);     // USEFUL! with cframe
                if (on(gene++)) add(fv, key + "lc", frame, label, cpostag);
                if (on(gene++)) add(fv, key + "lcl", frame, label, cpostag, lemma);
                if (on(gene++)) add(fv, key + "lp", frame, label, postag);
                if (on(gene++)) add(fv, key + "lpl", frame, label, postag, lemma);
                if (on(gene++)) add(fv, key + "lf", frame, label, form); // USEFUL! with cframe
                if (on(gene++)) add(fv, key + "ll", frame, label, lemma); // USEFUL! with cframe

                if (on(gene++)) add(fv, key + "c", frame, cpostag);
                if (on(gene++)) add(fv, key + "cl", frame, cpostag, lemma);
                if (on(gene++)) add(fv, key + "p", frame, postag);
                if (on(gene++)) add(fv, key + "pl", frame, postag, lemma);
                if (on(gene++)) add(fv, key + "f", frame, form);
                if (on(gene++)) add(fv, key + "cf", frame, cpostag, form);
                if (on(gene++)) add(fv, key + "pf", frame, postag, form);
                if (on(gene++)) add(fv, key + "l", frame, lemma);
            }
        }

        // Update gene count
        setLastGene(gene);

        // We only want unknown counted once
        if (fv.getValue(featureHandler.UNKNOWN) != 0)
            fv.setValue(featureHandler.UNKNOWN, 1);
    }
    

    static int[] positiveFilter(int[] list) {
        int count = 0;
        for (int i = 0; i < list.length; ++i) {
            if (list[i] > 0)
                ++count;
        }
        int[] result = new int[count];
        count = 0;
        for (int i = 0; i < list.length; ++i) {
            if (list[i]>0)
                result[count++] = list[i];
        }
        return result;
    }

    static Set<Integer> intArrayAsSet(int[] array) {
        Set<Integer> set = new HashSet<Integer>();
        for (int i : array)
            set.add(i);
        return set;
    }
}
